/* File name     :  nfivi_io.c
 * Author        :  Wentao Shang
 * 
 * Contents      :
 *    This file defines the ioctl command operations which is used by the user
 *    to communicate with the kernel modules. Functions in this module is used
 *    to define file_operation structure in the 'nfivi' module, which registers
 *    'nfivi' as a character device. 
 *
 *    See 'ioctl.c' file in the '/user' directory for the usage of user-mode 
 *    ioctl commands.
 *
 */

#include "nfivi_io.h"

// Device open lock
static int Device_Open = 0;

// Device open handle
int device_open(struct inode *inode, struct file *file)
{
	//printk ("device_open(%p)\n", file);
	if (Device_Open)   // This device can't be opened twice at each time
		return -EBUSY;
	Device_Open++;
	return 0;
}
EXPORT_SYMBOL(device_open);

// Device close handle
int device_release(struct inode *inode, struct file *file)
{
	//printk ("device_release(%p,%p)\n", inode, file);
	Device_Open--;
	return 0;
}
EXPORT_SYMBOL(device_release);

// Device ioctl dispatch function
int device_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_num, unsigned long ioctl_param)
{
	int retval;
	unsigned short *pTemp;
	unsigned short temp;
	long *pTime;
	
	retval = 0;
	
	switch (ioctl_num) {
		case IOCTL_SET_MOD:
			pTemp = (unsigned short *)ioctl_param;
			copy_from_user(&temp, pTemp, sizeof(unsigned short) );
			if( temp > 4096 ) {
				printk("ioctl error: Mod input too large!\n");
				retval = -EINPUT_TOO_LARGE;
			}
			else {
				Mod = temp;
				printk("ioctl: set Mod to %d\n", Mod);
				free_map_list(&MapList);
				printk("free old maplist success!\n");
				Current = 0; //reset Current counter
			}
			break;
		case IOCTL_GET_MOD:
			pTemp = (unsigned short *)ioctl_param;
			copy_to_user(pTemp, &Mod, sizeof(unsigned short) );
			printk("ioctl: get Mod by user\n");
			break;
		case IOCTL_SET_RES:
			pTemp = (unsigned short *)ioctl_param;
			copy_from_user(&temp, pTemp, sizeof(unsigned short) );
			if( temp >= Mod ) {
				printk("ioctl error: Res input too large!\n");
				retval = -EINPUT_TOO_LARGE;
			}
			else {
				Res = temp;
				printk("ioctl: set Res to %d\n", Res);
				free_map_list(&MapList);
				printk("free old maplist success!\n");
				Current = 0; //reset Current counter
			}
			break;
		case IOCTL_GET_RES:
			pTemp = (unsigned short *)ioctl_param;
			copy_to_user(pTemp, &Res, sizeof(unsigned short) );
			printk("ioctl: get Res by user\n");
			break;
		case IOCTL_SET_AUTOCONFIG:
			AutoConfig = 1;
			printk("ioctl: autoconfig enabled by user\n");
			free_map_list(&MapList);
			printk("free old maplist success!\n");
			Current = 0; //reset Current counter
			break;
		case IOCTL_UNSET_AUTOCONFIG:
			AutoConfig = 0;
			printk("ioctl: autoconfig disabled by user\n");
			free_map_list(&MapList);
			printk("free old maplist success!\n");
			Current = 0;   //reset Current counter
			Mod = 256;     //reset Mod
			printk("ioctl: reset Mod to %d\n", Mod);
			Res = 1;       //reset Res
			printk("ioctl: reset Res to %d\n", Res);
			free_map_list(&MapList);
			printk("free old maplist success!\n");
			Current = 0; //reset Current counter
			break;
		case IOCTL_SET_TIMEOUT:
			pTime = (long *)ioctl_param;
			copy_from_user(&TimeOut, pTime, sizeof(long) );
			printk("ioctl: set TimeOut to %ld\n", TimeOut);
			break;
		case IOCTL_GET_TIMEOUT:
			pTime = (long *)ioctl_param;
			copy_to_user(pTime, &TimeOut, sizeof(long) );
			printk("ioctl: get TimeOut by user\n");
			break;
		default:
			retval = -EINVAL;
	}
	return retval;
}
EXPORT_SYMBOL(device_ioctl);

MODULE_LICENSE("GPL");

